/**
 * @author Vitor Franco do Carmo (vitor.carmo@capgemini.com)
 * @since 21/05/2013
 *
 */
package br.com.vfcfw.commons.util;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import org.apache.log4j.Logger;

/**
 * Classe utilitária para obter a classe do tipo genérico da classe informada.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@capgemini.com)
 * @since 21/05/2013
 * 
 */
public class GenericsUtil {

    private static final Logger LOGGER = Logger.getLogger(GenericsUtil.class);

    /**
     * Construtor da classe.
     */
    private GenericsUtil() {

        super();
    }

    /**
     * Retorna a classe do primeiro tipo genérico da classe informada.
     * 
     * @param <T>
     * 
     * @param classe Classe que possui tipos genéricos.
     * @return Classe do tipo genérico.
     */
    @SuppressWarnings("rawtypes")
    public static Class getSuperClassGenericType(Class classe) {

        return getSuperClassGenericType(classe, 0);
    }

    /**
     * Retorna a classe do tipo genérico correspondente ao índice da classe informada.
     * 
     * @param classe Classe que possui tipos genéricos.
     * @param indice Índice do tipo genético (inicia com 0).
     * @return Classe do tipo genérico.
     */
    @SuppressWarnings("rawtypes")
    public static Class getSuperClassGenericType(Class classe, int indice) {

        Class classeRetorno = null;

        LogUtil.debug(LOGGER, "Obtendo o tipo genérico de índice: %s, da classe: %s", indice, classe.getSimpleName());

        try {

            Type genericType = classe.getGenericSuperclass();

            if (!(genericType instanceof ParameterizedType)) {
                genericType = ((Class) genericType).getGenericSuperclass();
            }

            classeRetorno = obterClasse(genericType, indice);

        } catch (Exception e) {
            LogUtil.error(LOGGER, e, "Erro ao obter o tipo genérico de índice: %s, da classe: %s", indice,
                classe.getSimpleName());
        }
        return classeRetorno;
    }

    /**
     * Retorna a classe to tipo genérico.
     * 
     * @param genericType {@link Type} Tipo genérico.
     * @param indice Índice do tipo genético (inicia com 0).
     * @return Classe do tipo genérico.
     */
    @SuppressWarnings("rawtypes")
    private static Class obterClasse(Type genericType, int indice) {

        Class classeRetorno = null;

        if (!(genericType instanceof ParameterizedType)) {

            classeRetorno = Object.class;
        } else {

            Type[] params = ((ParameterizedType) genericType).getActualTypeArguments();

            if (params == null) {
                ;
            } else {
                if (indice < 0 || indice > params.length) {

                    throw new IllegalArgumentException("Indice inv\u00e1lido!");
                } else {

                    classeRetorno = (Class) params[indice];
                }
            }
        }
        return classeRetorno;
    }

}
